/**
 * 
 */
package ee.logsimulator;

import ee.logsimulator.data.Actor;
import ee.logsimulator.data.Event;
import ee.logsimulator.data.Mode;
import ee.logsimulator.parser.data.Boss;
import ee.logsimulator.parser.data.Raid;
import ee.logsimulator.ui.AbstractPlayer;
import ee.logsimulator.ui.FilterPage;
import ee.logsimulator.ui.RaidPage;
import ee.logsimulator.ui.MessageDialog;
import ee.logsimulator.ui.Player;
import ee.logsimulator.ui.druid.RestoDruidPlayer;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.ScrollPane;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * @author Romet Piho
 * 
 */
public class Logsimulator extends JFrame {

  private static final long serialVersionUID = 1L;

  private Model model;

  private JPanel panel;

  private AbstractPlayer player;

  private JLabel label;
  private JLabel timer;
  private JButton simulate;
  private JButton filter;
  private JSlider slider;

  private String guildPage = "http://www.worldoflogs.com/guilds/2270/";
  private boolean simulating = false;

  public Logsimulator() {
    model = new Model();
    setLayout(new GridBagLayout());
    init();

    setMinimumSize(new Dimension(1024, 800));
    pack();
    setVisible(true);
    setTitle("Log simulator");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    addKeyListener();
  }

  private void addKeyListener() {
    KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    manager.addKeyEventDispatcher(new KeyEventDispatcher() {
      @Override
      public boolean dispatchKeyEvent(KeyEvent e) {
        if (slider != null && simulating) {
          if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            adjustSlider(-250);
          } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            adjustSlider(250);
          }
        }
        return false;
      }

      private void adjustSlider(int adjustment) {
        int cur = slider.getValue() + adjustment;
        if (cur < 0) {
          cur = 0;
        } else if (cur > slider.getMaximum()) {
          cur = slider.getMaximum();
        }
        slider.setValue(cur);
      }
    });
  }

  private void init() {
    drawMainPanel();
    updateSimulate();
  }

  public void selectRaid(Boss boss, Raid raid) {
    try {
      String url = "http://www.worldoflogs.com" + boss.link;
      model.addLogText(url.replace("dashboard", "xe"));
    } catch (Exception e) {
      parseException(e);
    }
    updateLabel();
    setTitle("Log simulator - " + raid.date + ": " + boss.name);
    updateSimulate();
  }

  private void drawMainPanel() {
    simulating = false;
    boolean addPanel = false;
    if (panel == null) {
      addPanel = true;
      panel = new JPanel(new GridBagLayout());
    } else {
      panel.removeAll();
    }

    label = new JLabel(getLabelText());
    label.setForeground(Color.WHITE);

    JPanel buttons = new JPanel(new FlowLayout());
    buttons.setBackground(Color.BLACK);
    JButton add = getAddButton();
    simulate = getSimulateButton();
    filter = getFilterButton();
    JButton clear = getClearButton();

    buttons.add(add);
    buttons.add(clear);
    buttons.add(filter);
    buttons.add(simulate);

    GridBagConstraints c = new GridBagConstraints();

    JPanel p = new JPanel(new GridBagLayout());
    p.setBackground(Color.BLACK);
    c.anchor = GridBagConstraints.NORTH;
    c.fill = GridBagConstraints.NONE;
    c.gridheight = 1;
    c.gridwidth = 1;
    c.gridx = 0;
    c.gridy = 0;
    c.weightx = 0;
    c.weighty = 0;
    p.add(label, c);

    c.anchor = GridBagConstraints.CENTER;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weightx = 1;
    c.weighty = 0;
    c.gridx = 0;
    c.gridy = 1;
    p.add(buttons, c);
    panel.add(p, c);
    panel.setBackground(Color.BLACK);

    if (addPanel) {
      c.anchor = GridBagConstraints.NORTH;
      c.fill = GridBagConstraints.BOTH;
      c.gridheight = GridBagConstraints.REMAINDER;
      c.gridwidth = GridBagConstraints.REMAINDER;
      c.gridx = 0;
      c.gridy = 0;
      c.weightx = 1;
      c.weighty = 1;
      add(panel, c);
    }
  }

  private JButton getFilterButton() {
    JButton simulate = new JButton("Filter");
    simulate.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        new FilterPage(Logsimulator.this, model);
      }
    });
    return simulate;
  }

  private JButton getSimulateButton() {
    JButton simulate = new JButton("Simulate");
    simulate.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        drawSimulatePanel();
      }
    });
    updateSimulate();
    return simulate;
  }

  private JButton getClearButton() {
    JButton clear = new JButton("Clear");
    clear.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        clear();
      }

    });
    return clear;
  }

  private void clear() {
    model.clearLogText();
    updateLabel();
    setTitle("Log simulator");
    updateSimulate();
  }

  private JButton getAddButton() {
    JButton add = new JButton("Add");
    add.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          if (!model.getEvents().isEmpty()) {
            int option = JOptionPane.showConfirmDialog(Logsimulator.this, "Do you want to clear previous events?", "Clear previous",
                JOptionPane.YES_NO_CANCEL_OPTION);
            switch (option) {
            case 0:
              clear();
              break;
            case 2:
              return;
            }
          }
          add();
          updateLabel();
        } catch (Exception e1) {
          parseException(e1);
        }
      }
    });
    return add;
  }

  public void updateLabel() {
    if (label != null && label.isVisible()) {
      label.setText(getLabelText());
    }
    if (slider != null && slider.isVisible()) {
      int time = slider.getValue();
      Calendar cal = Calendar.getInstance();
      cal.set(2011, 1, 1, 1, 0, 0);
      cal.set(Calendar.MILLISECOND, 0);
      cal.add(Calendar.MILLISECOND, time);
      SimpleDateFormat f = new SimpleDateFormat("mm:ss:SSS");
      String t = f.format(cal.getTime());
      slider.setToolTipText(t);
      if (timer != null && timer.isVisible()) {
        timer.setText(t);
      }
    }
  }

  private JButton getBackButton() {
    JButton newItem = new JButton("Back");
    newItem.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (player != null) {
          player.pause();
          slider.setValue(0);
        }
        drawMainPanel();
        pack();
      }
    });
    return newItem;
  }

  private JButton getNewButton() {
    JButton newItem = new JButton("New");
    newItem.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        model.clearLogText();
        if (player != null) {
          player.pause();
        }
        drawMainPanel();
        pack();
      }
    });
    return newItem;
  }

  private void add() {
    final MessageDialog d = new MessageDialog(this, "Enter guild", true);
    JLabel modeL = new JLabel("Parse mode: ");
    final JRadioButton heal = new JRadioButton("Healing");
    final JRadioButton damage = new JRadioButton("Damage");
    final JRadioButton druid = new JRadioButton("Druid healing simulation");
    heal.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        model.setMode(Mode.HEALING);
        heal.setSelected(true);
        damage.setSelected(false);
        druid.setSelected(false);
      }
    });
    damage.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        model.setMode(Mode.DAMAGE);
        heal.setSelected(false);
        damage.setSelected(true);
        druid.setSelected(false);
      }
    });
    druid.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        model.setMode(Mode.RESTO_DRUID);
        heal.setSelected(false);
        damage.setSelected(false);
        druid.setSelected(true);
      }
    });
    heal.setSelected(model.getMode() == Mode.HEALING);
    damage.setSelected(model.getMode() == Mode.DAMAGE);
    druid.setSelected(model.getMode() == Mode.RESTO_DRUID);

    JLabel l = new JLabel("WoL guild page url:");
    final JTextField url = new JTextField(23);
    url.setText(guildPage);
    JButton ok = new JButton("OK");
    ok.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          guildPage = url.getText();
          new RaidPage(Logsimulator.this, model, guildPage);
        } catch (Exception e1) {
          parseException(e1);
        }
        d.dispose();
      }
    });

    JPanel p1 = new JPanel();
    p1.setLayout(new BoxLayout(p1, BoxLayout.LINE_AXIS));
    p1.setAlignmentX(Component.CENTER_ALIGNMENT);
    p1.setBackground(Color.BLACK);

    modeL.setForeground(Color.WHITE);
    modeL.setAlignmentX(Component.CENTER_ALIGNMENT);
    heal.setForeground(Color.GREEN);
    heal.setBackground(Color.BLACK);
    heal.setFocusPainted(false);
    heal.setAlignmentX(Component.CENTER_ALIGNMENT);
    damage.setForeground(Color.RED);
    damage.setBackground(Color.BLACK);
    damage.setFocusPainted(false);
    damage.setAlignmentX(Component.CENTER_ALIGNMENT);
    druid.setForeground(Color.GREEN);
    druid.setBackground(Color.BLACK);
    druid.setFocusPainted(false);
    druid.setAlignmentX(Component.CENTER_ALIGNMENT);
    p1.add(modeL);
    p1.add(heal);
    p1.add(damage);
    p1.add(druid);

    JPanel p2 = new JPanel();
    p2.setLayout(new BoxLayout(p2, BoxLayout.PAGE_AXIS));
    p2.setBackground(Color.BLACK);

    JPanel pp = new JPanel(new FlowLayout());
    pp.setBackground(Color.BLACK);
    l.setForeground(Color.WHITE);
    pp.add(l);
    pp.add(url);

    p2.add(p1);
    p2.add(pp);
    p2.add(ok);

    d.add(p2, BorderLayout.CENTER);
    d.pack();
    d.setLocationRelativeTo(this);
    d.setVisible(true);
  }

  public void drawSimulatePanel() {
    panel.removeAll();
    Event first = model.getFirst();
    Event last = model.getLast();
    Long duration = last.getTimestamp().getTime() - first.getTimestamp().getTime();

    player = model.getMode() == Mode.RESTO_DRUID ? new RestoDruidPlayer(model) : new Player(model);
    JPanel buttons = new JPanel(new FlowLayout());

    slider = new JSlider(JSlider.HORIZONTAL, 0, duration.intValue(), 0);

    timer = new JLabel("00:00:000");
    timer.setFont(timer.getFont().deriveFont(20f));
    timer.setForeground(Color.WHITE);

    JButton back = getBackButton();
    JButton newItem = getNewButton();
    JButton reset = getResetDamageButton();
    JButton legend = getTargetLegendButton();
    JButton damage = getDamageLegendButton();
    JButton t = getTimerButton();
    final JButton pause = new JButton("Pause");
    final JButton play = getPlayButton(pause);
    setPauseButton(play, pause);

    setSlider(pause, play);

    buttons.add(back);
    buttons.add(newItem);
    buttons.add(t);
    buttons.add(play);
    buttons.add(pause);
    buttons.add(reset);
    buttons.add(legend);
    buttons.add(damage);

    GridBagConstraints c = new GridBagConstraints();
    c.anchor = GridBagConstraints.NORTH;
    c.fill = GridBagConstraints.BOTH;
    c.weightx = 1;
    c.weighty = 1;
    c.gridx = 0;
    c.gridy = 0;
    ScrollPane scroll = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
    scroll.setSize(panel.getWidth(), panel.getHeight());
    scroll.add(player);
    player.setScroll(scroll);
    panel.add(scroll, c);

    JPanel p = new JPanel(new GridBagLayout());
    c.anchor = GridBagConstraints.NORTH;
    c.fill = GridBagConstraints.NONE;
    c.weighty = 0;
    c.weightx = 0;
    c.gridx = 0;
    c.gridy = 0;
    c.gridheight = 1;
    p.add(buttons, c);

    c.anchor = GridBagConstraints.SOUTH;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weightx = 1;
    c.weighty = 0;
    c.gridx = 0;
    c.gridy = 1;
    p.add(slider, c);
    player.setSlider(slider);

    c.anchor = GridBagConstraints.SOUTH;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weightx = 1;
    c.gridx = 0;
    c.gridy = 1;
    c.gridheight = 1;
    panel.add(p, c);

    pack();
    simulating = true;
  }

  private JButton getTimerButton() {
    final JButton b = new JButton("Timer");
    b.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        MessageDialog d = new MessageDialog(Logsimulator.this, "Timer", false);
        d.setLocationRelativeTo(b);
        d.setLocation(d.getLocation().x, d.getLocation().y - 100);
        d.setBackground(Color.BLACK);
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
        p.setBackground(Color.BLACK);
        p.add(timer);
        d.add(p);
        d.pack();
        d.setVisible(true);
      }
    });
    return b;
  }

  private JButton getTargetLegendButton() {
    JButton legend = new JButton("Target legend");
    legend.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        MessageDialog d = new MessageDialog(Logsimulator.this, "Target color legend", false);
        d.setLocationRelativeTo(Logsimulator.this);
        d.setBackground(Color.BLACK);
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
        p.setBackground(Color.BLACK);
        for (Actor a : model.getTargetFilteredActors()) {
          JLabel label = new JLabel(a.getName());
          label.setForeground(model.getTartetColor(a));
          p.add(label);
        }
        d.add(p);
        d.pack();
        d.setVisible(true);
      }
    });
    return legend;
  }

  private JButton getDamageLegendButton() {
    JButton legend = new JButton("Damage legend");
    legend.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        MessageDialog d = new MessageDialog(Logsimulator.this, "Damage color legend", false);
        d.setLocationRelativeTo(Logsimulator.this);
        d.setBackground(Color.BLACK);
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
        p.setBackground(Color.BLACK);

        JLabel normal = new JLabel("Normal damage or healing");
        normal.setForeground(Color.WHITE);
        p.add(normal);

        JLabel crit = new JLabel("Critical damage or healing");
        crit.setForeground(Color.YELLOW);
        p.add(crit);

        JLabel over = new JLabel("Overkill or overheal");
        over.setForeground(Color.RED);
        p.add(over);

        JLabel block = new JLabel("Blocked damage");
        block.setForeground(Color.ORANGE);
        p.add(block);

        JLabel absorb = new JLabel("Absorbed damage");
        absorb.setForeground(Color.GREEN);
        p.add(absorb);

        JLabel resist = new JLabel("Resisted damage");
        resist.setForeground(Color.CYAN);
        p.add(resist);

        d.add(p);
        d.pack();
        d.setVisible(true);
      }
    });
    return legend;
  }

  private void setSlider(final JButton pause, final JButton play) {
    slider.addChangeListener(new ChangeListener() {
      @Override
      public void stateChanged(ChangeEvent e) {
        if (slider.getValue() == slider.getMaximum()) {
          player.pause();
          play.setVisible(true);
          pause.setVisible(false);
        }
        updateLabel();
        player.update();
      }
    });
  }

  private void setPauseButton(final JButton play, final JButton pause) {
    pause.setVisible(false);
    pause.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        player.pause();
        play.setVisible(true);
        pause.setVisible(false);
      }
    });
    pause.setBackground(Color.RED);
  }

  private JButton getPlayButton(final JButton pause) {
    final JButton play = new JButton("  Play  ");
    play.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (slider.getValue() == slider.getMaximum()) {
          slider.setValue(0);
        }
        player.play();
        play.setVisible(false);
        pause.setVisible(true);
      }
    });
    play.setBackground(Color.GREEN);
    return play;
  }

  private JButton getResetDamageButton() {
    JButton reset = new JButton("Reset damage");
    reset.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        player.reset(slider.getValue());
      }
    });
    return reset;
  }

  protected Model getModel() {
    return model;
  }

  private String getLabelText() {
    if (model.getPlayerEvents().isEmpty()) {
      return model.getEvents().isEmpty() ? "Currently there are no events stored."
          : "Everything has been filtered out. Change filter settings.";
    }
    Event f = model.getFirst();
    Event l = model.getLast();
    Calendar cal = Calendar.getInstance();
    cal.set(2011, 1, 1, 0, 0, 0);
    cal.set(Calendar.MILLISECOND, 0);
    cal.add(Calendar.MILLISECOND, (int) (l.getTimestamp().getTime() - f.getTimestamp().getTime()));
    SimpleDateFormat format = new SimpleDateFormat("mm:ss");
    return "Currently stored " + format.format(cal.getTime()) + " of events for " + model.getPlayerEvents().size() + " sources";
  }

  public void parseException(Exception e) {
    JDialog dialog = new JDialog(Logsimulator.this, true);
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
    JLabel l = new JLabel();
    l.setText(e.toString());
    p.add(l);
    for (StackTraceElement el : e.getStackTrace()) {
      l = new JLabel();
      l.setText(el.toString());
      p.add(l);
    }

    dialog.add(p);
    dialog.pack();
    dialog.setVisible(true);
  }

  public static void main(String[] args) {
    new Logsimulator();
  }

  public void updateSimulate() {
    if (simulate != null) {
      simulate.setEnabled(!model.getPlayerEvents().isEmpty());
    }
  }

  public void setGuildPage(String guildPage) {
    this.guildPage = guildPage;
  }

}
